from datetime import datetime, date
from typing import List

from service.models import Event, PredictionFact, Procedure
from users.models import User, SpecializationType


def check_week(day, month, year, week) -> bool:
    return week == date(year, month, day).isocalendar().week


def count_event_hours(event: Event):
    interval = datetime.combine(event.date, event.end_time) - datetime.combine(event.date, event.start_time)
    return interval.total_seconds() // 3600


def form_week_work_events(users: List[User], events: List[Event], week: int):
    week_events = {}
    for user in users:
        week_events[user.id] = filter(
            lambda x: x.user == user and check_week(x.day, x.month.month, x.month.year, week) and x.kind in [
                'work_time'],
            events
        )

    return week_events


def form_user_constraints(users: List[User], week_events: List[Event]) -> dict[int, int]:
    week_hours = {}

    for user in users:
        user_week_hours = sum([count_event_hours(event) for event in week_events[user.id]])
        week_hours[user.id] = user_week_hours

    return week_hours


def form_prediction_constraints(
        predictions: List[PredictionFact],
        procedures: List[Procedure],
        specialisations: List[SpecializationType]
) -> List[int]:
    def transform_to_hours(qtty, specialization) -> int:
        procedure_hours = 0
        for x in procedures:
            if x.specialization.id == specialization.id:
                procedure_hours = (qtty * x.duration.total_seconds()) // 3600
                break
        return procedure_hours

    def mapper(specialisation: SpecializationType) -> int:
        for prediction in predictions:
            if prediction.specialization.id == specialisation.id:
                return transform_to_hours(prediction.qtty_predicted, specialisation) \
                    if prediction.qtty_predicted is not None \
                    else transform_to_hours(prediction.qtty, specialisation)
        return 0

    constraints = [mapper(specialisation) for specialisation in specialisations]
    return constraints


def form_events_constraints(events: List[Event]) -> list[float]:
    return [count_event_hours(event) for event in events]
